package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author atguigu-mqx
 */
@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${authUrls.url}")
    public String authUrls;  // trade.html,myOrder.html,list.html

    //  创建一个对象
    private AntPathMatcher matcher = new AntPathMatcher();
    /**
     *
     * @param exchange 能够获取到web 的请求与响应的对象
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  获取用户访问的url
        //  exchange.getRequest().getURI()=http://localhost/api/product/inner/getSkuInfo/17
        String path = exchange.getRequest().getURI().getPath();

        //  获取到响应对象
        ServerHttpResponse response = exchange.getResponse();
        //  获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //  path = /api/product/inner/getSkuInfo/17
        //  path = list.html?category3Id=61
        //  判断用户访问的是否是内部数据接口 /**/inner/**
        //  第一个参数相当于key！    第二个参数： 整体获取到的path 路径 ！是 path ，还是
        if (matcher.match("/**/inner/**",path)){
            //  路径匹配：
            //  停止运行
            return out(response, ResultCodeEnum.PERMISSION);
        }
        //  获取用户Id
        String userId = this.getUserId(request);
        //  获取用户的临时Id
        String userTempId = this.getUserTempId(request);
        if ("-1".equals(userId)){
            //  停止运行 没有权限
            return out(response, ResultCodeEnum.PERMISSION);
        }
        //  判断：/api/**/auth/**
        if (matcher.match("/api/**/auth/**",path)){
            //  路径匹配：
            //  用户必须是登录的情况下才能访问，不是未登录不能！
            if (StringUtils.isEmpty(userId)){
                //  需要登录
                //  停止运行
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }
        //  用户访问web-all中哪些控制器时，需要跳转到登录页面！
        String[] split = authUrls.split(",");
        for (String url : split) {
            //  判断  用户访问的控制器，在authUrls 中存在， 并且用户是未登录的情况下！
            if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                //  提示，跳转到登录！
                //  设置一些响应的参数！
                response.setStatusCode(HttpStatus.SEE_OTHER);
                //  设置请求头 ：
                response.getHeaders().set(HttpHeaders.LOCATION,"http://www.gmall.com/login.html?originUrl="+request.getURI());
                //  代表重定向！
                return response.setComplete();
            }
        }

        //  将用户Id 放入请求头中！
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)){
            if (!StringUtils.isEmpty(userId) ){
                //  判断用户Id 不为空
                request.mutate().header("userId", userId).build();
            }
            if (!StringUtils.isEmpty(userTempId)){
                //  判断临时用户Id 不为空
                request.mutate().header("userTempId", userTempId).build();
            }
            //  返回
            return chain.filter(exchange.mutate().request(request).build());
        }
        //  默认返回：
        return chain.filter(exchange);
    }

    //  返回用户的临时Id
    private String getUserTempId(ServerHttpRequest request) {
        //  临时用户Id 被存入到cookie 中！    也有可能存在header中！
        String userTempId = "";
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        if (httpCookie!=null){
            userTempId = httpCookie.getValue();
        }else {
            List<String> list = request.getHeaders().get("userTempId");
            if (!CollectionUtils.isEmpty(list)){
                userTempId = list.get(0);
            }
        }
        //  返回数据
        return userTempId;
    }

    //  将用户提示信息输入
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  输入的内容在 resultCodeEnum中！ 如果有字符的，可以设置一下字符集！
        Result<String> result = Result.build(null, resultCodeEnum);
        String str = JSON.toJSONString(result);
        DataBuffer wrap = response.bufferFactory().wrap(str.getBytes());
        //  设置一下请求头，还有字符集
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(wrap));
    }

    //  获取用户Id
    private String getUserId(ServerHttpRequest request) {
        String token = "";
        //  用户Id 在缓存中：缓存的key 是由token 组成，因此，必须先获取到token！
        //  token 在 cookie 中存在，同时也有可能在header 中！
        HttpCookie httpCookie = request.getCookies().getFirst("token");
        if (httpCookie!=null){
            token = httpCookie.getValue();
        }else {
            List<String> list = request.getHeaders().get("token");
            if (!CollectionUtils.isEmpty(list)){
                token = list.get(0);
            }
        }
        //  获取到token 之后，组成缓存的key 来获取缓存中的数据！
        if(!StringUtils.isEmpty(token)){
            String userKey = "user:login:"+token;
            String strJson = (String) this.redisTemplate.opsForValue().get(userKey);
            // 需要将strJson 在转换为JSONObject
            //  JSONObject jsonObject = JSON.parseObject(strJson, JSONObject.class);
            JSONObject jsonObject = JSONObject.parseObject(strJson);

            String ip = (String) jsonObject.get("ip");
            //  判断Ip 地址是否一致！
            if (ip.equals(IpUtil.getGatwayIpAddress(request))){
                String userId = (String) jsonObject.get("userId");
                return userId;
            }else {
                //  ip 地址没有匹配正确！
                return "-1";
            }
        }
        return null;
    }
}
